winsafe\gui\native_controls/
combo_box.rs

1use std::any::Any;
2use std::marker::PhantomPinned;
3use std::pin::Pin;
4use std::sync::Arc;
5
6use crate::co;
7use crate::decl::*;
8use crate::gui::{collections::*, events::*, privs::*, *};
9use crate::prelude::*;
10
11struct ComboBoxObj {
12	base: BaseCtrl,
13	events: ComboBoxEvents,
14	_pin: PhantomPinned,
15}
16
17native_ctrl! { ComboBox: ComboBoxObj => ComboBoxEvents;
18	/// Native
19	/// [combo box](https://learn.microsoft.com/en-us/windows/win32/controls/about-combo-boxes)
20	/// control.
21}
22
23impl ComboBox {
24	/// Instantiates a new `ComboBox` object, to be created on the parent window
25	/// with [`HWND::CreateWindowEx`](crate::HWND::CreateWindowEx).
26	///
27	/// # Panics
28	///
29	/// Panics if the parent window was already created – that is, you cannot
30	/// dynamically create a `ComboBox` in an event closure.
31	///
32	/// Panics if vertical resizing behavior is
33	/// [`Vert::Resize`](crate::gui::Vert::Resize).
34	///
35	/// # Examples
36	///
37	/// ```no_run
38	/// use winsafe::{self as w, prelude::*, gui};
39	///
40	/// let wnd: gui::WindowMain; // initialized somewhere
41	/// # let wnd = gui::WindowMain::new(gui::WindowMainOpts::default());
42	///
43	/// let cmb = gui::ComboBox::new(
44	///     &wnd,
45	///     gui::ComboBoxOpts {
46	///         position: (10, 10),
47	///         width: 140,
48	///         items: vec![
49	///             "Avocado".to_owned(),
50	///             "Banana".to_owned(),
51	///             "Grape".to_owned(),
52	///             "Orange".to_owned(),
53	///         ],
54	///         selected_item: Some(0),
55	///         ..Default::default()
56	///     },
57	/// );
58	/// ```
59	#[must_use]
60	pub fn new(parent: &(impl GuiParent + 'static), opts: ComboBoxOpts) -> Self {
61		if opts.resize_behavior.1 == Vert::Resize {
62			panic!("ComboBox cannot be resized with Vert::Resize.");
63		}
64
65		let ctrl_id = auto_id::set_if_zero(opts.ctrl_id);
66		let new_self = Self(Arc::pin(ComboBoxObj {
67			base: BaseCtrl::new(ctrl_id),
68			events: ComboBoxEvents::new(parent, ctrl_id),
69			_pin: PhantomPinned,
70		}));
71
72		let self2 = new_self.clone();
73		let parent2 = parent.clone();
74		parent
75			.as_ref()
76			.before_on()
77			.wm(parent.as_ref().wnd_ty().creation_msg(), move |_| {
78				self2.0.base.create_window(
79					opts.window_ex_style,
80					"COMBOBOX",
81					None,
82					opts.window_style | opts.control_style.into(),
83					opts.position.into(),
84					SIZE::with(opts.width, 0),
85					&parent2,
86				);
87				ui_font::set(self2.hwnd());
88				self2.items().add(&opts.items)?;
89				self2.items().select(opts.selected_item);
90				parent2
91					.as_ref()
92					.add_to_layout(self2.hwnd(), opts.resize_behavior);
93				Ok(0) // ignored
94			});
95
96		new_self
97	}
98
99	/// Instantiates a new `ComboBox` object, to be loaded from a dialog
100	/// resource with [`HWND::GetDlgItem`](crate::HWND::GetDlgItem).
101	///
102	/// # Panics
103	///
104	/// Panics if the parent dialog was already created – that is, you cannot
105	/// dynamically create a `ComboBox` in an event closure.
106	///
107	/// Panics if vertical resizing behavior is
108	/// [`Vert::Resize`](crate::gui::Vert::Resize).
109	#[must_use]
110	pub fn new_dlg(
111		parent: &(impl GuiParent + 'static),
112		ctrl_id: u16,
113		resize_behavior: (Horz, Vert),
114	) -> Self {
115		if resize_behavior.1 == Vert::Resize {
116			panic!("ComboBox cannot be resized with Vert::Resize.");
117		}
118
119		let new_self = Self(Arc::pin(ComboBoxObj {
120			base: BaseCtrl::new(ctrl_id),
121			events: ComboBoxEvents::new(parent, ctrl_id),
122			_pin: PhantomPinned,
123		}));
124
125		let self2 = new_self.clone();
126		let parent2 = parent.clone();
127		parent.as_ref().before_on().wm_init_dialog(move |_| {
128			self2.0.base.assign_dlg(&parent2);
129			parent2
130				.as_ref()
131				.add_to_layout(self2.hwnd(), resize_behavior);
132			Ok(true) // ignored
133		});
134
135		new_self
136	}
137
138	/// Item methods.
139	#[must_use]
140	pub const fn items(&self) -> ComboBoxItems<'_> {
141		ComboBoxItems::new(self)
142	}
143}
144
145/// Options to create a [`ComboBox`](crate::gui::ComboBox) programmatically with
146/// [`ComboBox::new`](crate::gui::ComboBox::new).
147pub struct ComboBoxOpts {
148	/// Left and top position coordinates of control within parent's client
149	/// area, to be
150	/// [created](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw).
151	///
152	/// Defaults to `gui::dpi(0, 0)`.
153	pub position: (i32, i32),
154	/// Control width to be
155	/// [created](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw).
156	///
157	/// Defaults to `gui::dpi_x(120)`.
158	pub width: i32,
159	/// Combo box styles to be
160	/// [created](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw).
161	///
162	/// Defaults to `CBS::DROPDOWNLIST`.
163	///
164	/// Suggestions:
165	/// * replace with `CBS::DROPDOWN` to allow the user to type a text;
166	/// * add `CBS::SORT` to automatically sort the items.
167	pub control_style: co::CBS,
168	/// Window styles to be
169	/// [created](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw).
170	///
171	/// Defaults to `WS::CHILD | WS::GROUP | WS::TABSTOP | WS::VISIBLE`.
172	pub window_style: co::WS,
173	/// Extended window styles to be
174	/// [created](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw).
175	///
176	/// Defaults to `WS_EX::LEFT`.
177	pub window_ex_style: co::WS_EX,
178
179	/// The control ID.
180	///
181	/// Defaults to an auto-generated ID.
182	pub ctrl_id: u16,
183	/// Horizontal and vertical behavior of the control when the parent window
184	/// is resized.
185	///
186	/// **Note:** A `ComboBox` cannot be resized vertically, so it will panic if
187	/// you use `Vert::Resize`.
188	///
189	/// Defaults to `(gui::Horz::None, gui::Vert::None)`.
190	pub resize_behavior: (Horz, Vert),
191
192	/// Items to be added.
193	///
194	/// Defaults to none.
195	pub items: Vec<String>,
196	/// Index of the item initially selected. The item must exist.
197	///
198	/// Defaults to `None`.
199	pub selected_item: Option<u32>,
200}
201
202impl Default for ComboBoxOpts {
203	fn default() -> Self {
204		Self {
205			position: dpi(0, 0),
206			width: dpi_x(120),
207			control_style: co::CBS::DROPDOWNLIST,
208			window_style: co::WS::CHILD | co::WS::GROUP | co::WS::TABSTOP | co::WS::VISIBLE,
209			window_ex_style: co::WS_EX::LEFT,
210			ctrl_id: 0,
211			resize_behavior: (Horz::None, Vert::None),
212			items: Vec::new(),
213			selected_item: None,
214		}
215	}
216}